using System.Net;
using System.Net.Sockets;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Microsoft.Extensions.Logging;
using ReactiveChannelMessaging.Core;

namespace ReactiveChannelMessaging.Communication;

/// <summary>
/// 响应式UDP通讯管理器
/// </summary>
public class ReactiveUdpCommunication : IAsyncDisposable
{
    private readonly ILogger<ReactiveUdpCommunication> _logger;
    private readonly ReactiveMessageBus _messageBus;
    private readonly Subject<UdpDataMessage> _receivedDataSubject;
    private readonly Subject<UdpStatusMessage> _statusSubject;
    private readonly List<IDisposable> _subscriptions;
    private readonly CancellationTokenSource _cancellationTokenSource;
    
    private UdpClient? _udpClient;
    private IPEndPoint? _localEndPoint;
    private IPEndPoint? _remoteEndPoint;
    private Task? _receiveTask;

    public int LocalPort { get; }
    public string? RemoteHost { get; }
    public int? RemotePort { get; }
    public bool IsListening => _udpClient != null && _receiveTask != null && !_receiveTask.IsCompleted;

    public ReactiveUdpCommunication(
        int localPort,
        ILogger<ReactiveUdpCommunication> logger,
        ReactiveMessageBus messageBus,
        string? remoteHost = null,
        int? remotePort = null)
    {
        LocalPort = localPort;
        RemoteHost = remoteHost;
        RemotePort = remotePort;
        
        _logger = logger;
        _messageBus = messageBus;
        _receivedDataSubject = new Subject<UdpDataMessage>();
        _statusSubject = new Subject<UdpStatusMessage>();
        _subscriptions = new List<IDisposable>();
        _cancellationTokenSource = new CancellationTokenSource();

        SetupMessageSubscriptions();
    }

    /// <summary>
    /// 接收到的UDP数据流
    /// </summary>
    public IObservable<UdpDataMessage> ReceivedData => _receivedDataSubject.AsObservable();

    /// <summary>
    /// UDP状态变化流
    /// </summary>
    public IObservable<UdpStatusMessage> StatusChanges => _statusSubject.AsObservable();

    /// <summary>
    /// 启动UDP通讯
    /// </summary>
    public async Task<bool> StartAsync()
    {
        try
        {
            _localEndPoint = new IPEndPoint(IPAddress.Any, LocalPort);
            _udpClient = new UdpClient(_localEndPoint);

            if (!string.IsNullOrEmpty(RemoteHost) && RemotePort.HasValue)
            {
                _remoteEndPoint = new IPEndPoint(IPAddress.Parse(RemoteHost), RemotePort.Value);
                _udpClient.Connect(_remoteEndPoint);
                
                await PublishStatusMessage(UdpConnectionStatus.Connected, 
                    $"Connected to {RemoteHost}:{RemotePort}");
            }
            else
            {
                await PublishStatusMessage(UdpConnectionStatus.Listening, 
                    $"Listening on port {LocalPort}");
            }

            // 启动数据接收任务
            _receiveTask = Task.Run(async () => await ReceiveDataAsync(_cancellationTokenSource.Token));
            
            _logger.LogInformation("UDP communication started on port {LocalPort}", LocalPort);
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to start UDP communication");
            await PublishStatusMessage(UdpConnectionStatus.Error, ex.Message);
            return false;
        }
    }

    /// <summary>
    /// 停止UDP通讯
    /// </summary>
    public async Task StopAsync()
    {
        try
        {
            _cancellationTokenSource.Cancel();

            if (_receiveTask != null)
            {
                await _receiveTask;
                _receiveTask = null;
            }

            if (_udpClient != null)
            {
                _udpClient.Close();
                _udpClient.Dispose();
                _udpClient = null;
            }

            await PublishStatusMessage(UdpConnectionStatus.Stopped, "UDP communication stopped");
            _logger.LogInformation("UDP communication stopped");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error stopping UDP communication");
        }
    }

    /// <summary>
    /// 发送数据到指定端点
    /// </summary>
    public async Task<bool> SendAsync(byte[] data, IPEndPoint? remoteEndPoint = null)
    {
        try
        {
            if (_udpClient == null)
            {
                _logger.LogWarning("UDP client is not initialized");
                return false;
            }

            int bytesSent;
            string targetEndPoint;

            if (remoteEndPoint != null)
            {
                bytesSent = await _udpClient.SendAsync(data, data.Length, remoteEndPoint);
                targetEndPoint = remoteEndPoint.ToString();
            }
            else if (_remoteEndPoint != null)
            {
                bytesSent = await _udpClient.SendAsync(data, data.Length);
                targetEndPoint = _remoteEndPoint.ToString();
            }
            else
            {
                _logger.LogWarning("No remote endpoint specified for UDP send");
                return false;
            }

            var dataMessage = new UdpDataMessage(
                Guid.NewGuid(),
                DateTime.UtcNow,
                "UdpCommunication",
                targetEndPoint,
                data,
                UdpDataDirection.Sent
            );

            await _messageBus.PublishAsync(dataMessage);
            _logger.LogDebug("Sent {BytesSent} bytes to {TargetEndPoint}", bytesSent, targetEndPoint);
            
            return bytesSent == data.Length;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send UDP data");
            return false;
        }
    }

    /// <summary>
    /// 发送数据到指定主机和端口
    /// </summary>
    public async Task<bool> SendAsync(byte[] data, string host, int port)
    {
        try
        {
            var endPoint = new IPEndPoint(IPAddress.Parse(host), port);
            return await SendAsync(data, endPoint);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Invalid host or port: {Host}:{Port}", host, port);
            return false;
        }
    }

    /// <summary>
    /// 发送字符串数据
    /// </summary>
    public async Task<bool> SendStringAsync(string data, IPEndPoint? remoteEndPoint = null, System.Text.Encoding? encoding = null)
    {
        encoding ??= System.Text.Encoding.UTF8;
        var bytes = encoding.GetBytes(data);
        return await SendAsync(bytes, remoteEndPoint);
    }

    /// <summary>
    /// 发送字符串数据到指定主机和端口
    /// </summary>
    public async Task<bool> SendStringAsync(string data, string host, int port, System.Text.Encoding? encoding = null)
    {
        encoding ??= System.Text.Encoding.UTF8;
        var bytes = encoding.GetBytes(data);
        return await SendAsync(bytes, host, port);
    }

    /// <summary>
    /// 广播数据到指定端口（局域网内所有主机）
    /// </summary>
    public async Task<bool> BroadcastAsync(byte[] data, int port)
    {
        try
        {
            var broadcastEndPoint = new IPEndPoint(IPAddress.Broadcast, port);
            
            // 启用广播
            if (_udpClient != null)
            {
                _udpClient.EnableBroadcast = true;
            }
            
            return await SendAsync(data, broadcastEndPoint);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to broadcast UDP data");
            return false;
        }
    }

    /// <summary>
    /// 广播字符串数据
    /// </summary>
    public async Task<bool> BroadcastStringAsync(string data, int port, System.Text.Encoding? encoding = null)
    {
        encoding ??= System.Text.Encoding.UTF8;
        var bytes = encoding.GetBytes(data);
        return await BroadcastAsync(bytes, port);
    }

    /// <summary>
    /// 组播数据到指定组播地址
    /// </summary>
    public async Task<bool> MulticastAsync(byte[] data, string multicastAddress, int port)
    {
        try
        {
            var multicastEndPoint = new IPEndPoint(IPAddress.Parse(multicastAddress), port);
            
            // 加入组播组
            if (_udpClient != null && IPAddress.TryParse(multicastAddress, out var addr))
            {
                _udpClient.JoinMulticastGroup(addr);
            }
            
            return await SendAsync(data, multicastEndPoint);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to multicast UDP data to {MulticastAddress}:{Port}", 
                multicastAddress, port);
            return false;
        }
    }

    private async Task ReceiveDataAsync(CancellationToken cancellationToken)
    {
        try
        {
            while (!cancellationToken.IsCancellationRequested && _udpClient != null)
            {
                var result = await _udpClient.ReceiveAsync();
                
                var dataMessage = new UdpDataMessage(
                    Guid.NewGuid(),
                    DateTime.UtcNow,
                    "UdpCommunication",
                    result.RemoteEndPoint.ToString(),
                    result.Buffer,
                    UdpDataDirection.Received
                );

                _receivedDataSubject.OnNext(dataMessage);
                await _messageBus.PublishAsync(dataMessage);

                _logger.LogDebug("Received {BytesReceived} bytes from {RemoteEndPoint}", 
                    result.Buffer.Length, result.RemoteEndPoint);
            }
        }
        catch (ObjectDisposedException)
        {
            // UDP客户端已释放，正常退出
        }
        catch (SocketException ex) when (cancellationToken.IsCancellationRequested)
        {
            // 取消操作导致的异常，正常退出
            _logger.LogDebug("UDP receive cancelled: {Message}", ex.Message);
        }
        catch (Exception ex)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                _logger.LogError(ex, "Error receiving UDP data");
                await PublishStatusMessage(UdpConnectionStatus.Error, ex.Message);
            }
        }
    }

    private void SetupMessageSubscriptions()
    {
        // 监听来自消息总线的UDP命令
        var udpCommandSubscription = _messageBus.CommandMessages
            .Where(cmd => cmd.Command.StartsWith("udp_"))
            .Subscribe(async cmd =>
            {
                try
                {
                    await ProcessUdpCommand(cmd);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error processing UDP command: {Command}", cmd.Command);
                }
            });

        _subscriptions.Add(udpCommandSubscription);

        // 数据分析和协议识别
        var dataAnalysisSubscription = ReceivedData
            .GroupBy(msg => msg.RemoteEndPoint)
            .SelectMany(group => 
                group.Buffer(TimeSpan.FromSeconds(1)) // 1秒内来自同一端点的数据
                    .Where(buffer => buffer.Count > 0)
                    .Select(buffer => new
                    {
                        EndPoint = group.Key,
                        MessageCount = buffer.Count,
                        TotalBytes = buffer.Sum(msg => msg.Data.Length),
                        FirstMessage = buffer.First(),
                        LastMessage = buffer.Last()
                    }))
            .Subscribe(analysis =>
            {
                _logger.LogInformation(
                    "UDP traffic analysis - EndPoint: {EndPoint}, Messages: {MessageCount}, Bytes: {TotalBytes}",
                    analysis.EndPoint, analysis.MessageCount, analysis.TotalBytes);
            });

        _subscriptions.Add(dataAnalysisSubscription);
    }

    private async Task ProcessUdpCommand(CommandMessage command)
    {
        var response = command.Command.ToLowerInvariant() switch
        {
            "udp_send" => await HandleSendCommand(command),
            "udp_send_string" => await HandleSendStringCommand(command),
            "udp_broadcast" => await HandleBroadcastCommand(command),
            "udp_multicast" => await HandleMulticastCommand(command),
            "udp_status" => HandleStatusCommand(),
            "udp_stop" => await HandleStopCommand(),
            "udp_start" => await HandleStartCommand(),
            _ => new { Success = false, Error = $"Unknown UDP command: {command.Command}" }
        };

        var responseMessage = new ResponseMessage(
            Guid.NewGuid(),
            DateTime.UtcNow,
            "UdpCommunication",
            command.Id,
            response.GetType().GetProperty("Success")?.GetValue(response) as bool? ?? false,
            response
        );

        await _messageBus.PublishAsync(responseMessage);
    }

    private async Task<object> HandleSendCommand(CommandMessage command)
    {
        if (command.Parameters?.TryGetValue("data", out var data) == true)
        {
            byte[] bytes = data switch
            {
                byte[] b => b,
                string s => System.Text.Encoding.UTF8.GetBytes(s),
                _ => System.Text.Encoding.UTF8.GetBytes(data.ToString() ?? "")
            };

            IPEndPoint? endPoint = null;
            if (command.Parameters.TryGetValue("host", out var host) && 
                command.Parameters.TryGetValue("port", out var port))
            {
                if (IPAddress.TryParse(host.ToString(), out var addr) && 
                    int.TryParse(port.ToString(), out var portNum))
                {
                    endPoint = new IPEndPoint(addr, portNum);
                }
            }

            var success = await SendAsync(bytes, endPoint);
            return new { Success = success, BytesSent = bytes.Length, TargetEndPoint = endPoint?.ToString() };
        }
        return new { Success = false, Error = "Invalid data parameter" };
    }

    private async Task<object> HandleSendStringCommand(CommandMessage command)
    {
        if (command.Parameters?.TryGetValue("text", out var text) == true)
        {
            IPEndPoint? endPoint = null;
            if (command.Parameters.TryGetValue("host", out var host) && 
                command.Parameters.TryGetValue("port", out var port))
            {
                if (IPAddress.TryParse(host.ToString(), out var addr) && 
                    int.TryParse(port.ToString(), out var portNum))
                {
                    endPoint = new IPEndPoint(addr, portNum);
                }
            }

            var success = await SendStringAsync(text.ToString() ?? "", endPoint);
            return new { Success = success, TextSent = text, TargetEndPoint = endPoint?.ToString() };
        }
        return new { Success = false, Error = "Invalid text parameter" };
    }

    private async Task<object> HandleBroadcastCommand(CommandMessage command)
    {
        if (command.Parameters?.TryGetValue("data", out var data) == true &&
            command.Parameters.TryGetValue("port", out var portObj) == true &&
            int.TryParse(portObj.ToString(), out var port))
        {
            byte[] bytes = data switch
            {
                byte[] b => b,
                string s => System.Text.Encoding.UTF8.GetBytes(s),
                _ => System.Text.Encoding.UTF8.GetBytes(data.ToString() ?? "")
            };

            var success = await BroadcastAsync(bytes, port);
            return new { Success = success, BytesSent = bytes.Length, BroadcastPort = port };
        }
        return new { Success = false, Error = "Invalid data or port parameter" };
    }

    private async Task<object> HandleMulticastCommand(CommandMessage command)
    {
        if (command.Parameters?.TryGetValue("data", out var data) == true &&
            command.Parameters.TryGetValue("address", out var address) == true &&
            command.Parameters.TryGetValue("port", out var portObj) == true &&
            int.TryParse(portObj.ToString(), out var port))
        {
            byte[] bytes = data switch
            {
                byte[] b => b,
                string s => System.Text.Encoding.UTF8.GetBytes(s),
                _ => System.Text.Encoding.UTF8.GetBytes(data.ToString() ?? "")
            };

            var success = await MulticastAsync(bytes, address.ToString() ?? "", port);
            return new { Success = success, BytesSent = bytes.Length, MulticastAddress = address, Port = port };
        }
        return new { Success = false, Error = "Invalid data, address, or port parameter" };
    }

    private object HandleStatusCommand()
    {
        return new
        {
            Success = true,
            LocalPort = LocalPort,
            RemoteHost = RemoteHost,
            RemotePort = RemotePort,
            IsListening = IsListening,
            LocalEndPoint = _localEndPoint?.ToString(),
            RemoteEndPoint = _remoteEndPoint?.ToString()
        };
    }

    private async Task<object> HandleStopCommand()
    {
        await StopAsync();
        return new { Success = true, Message = "UDP communication stopped" };
    }

    private async Task<object> HandleStartCommand()
    {
        var success = await StartAsync();
        return new { Success = success, Message = success ? "UDP communication started" : "Failed to start" };
    }

    private async Task PublishStatusMessage(UdpConnectionStatus status, string description)
    {
        var statusMessage = new UdpStatusMessage(
            Guid.NewGuid(),
            DateTime.UtcNow,
            "UdpCommunication",
            LocalPort,
            status,
            description
        );

        _statusSubject.OnNext(statusMessage);
        await _messageBus.PublishAsync(statusMessage);
    }

    public async ValueTask DisposeAsync()
    {
        await StopAsync();
        
        foreach (var subscription in _subscriptions)
        {
            subscription.Dispose();
        }
        
        _receivedDataSubject.Dispose();
        _statusSubject.Dispose();
        _cancellationTokenSource.Dispose();
    }
}

/// <summary>
/// UDP数据消息
/// </summary>
public record UdpDataMessage(
    Guid Id,
    DateTime Timestamp,
    string Source,
    string RemoteEndPoint,
    byte[] Data,
    UdpDataDirection Direction
) : ReactiveChannelMessaging.Core.Message(Id, Timestamp, Source);

/// <summary>
/// UDP状态消息
/// </summary>
public record UdpStatusMessage(
    Guid Id,
    DateTime Timestamp,
    string Source,
    int LocalPort,
    UdpConnectionStatus Status,
    string Description
) : ReactiveChannelMessaging.Core.Message(Id, Timestamp, Source);

/// <summary>
/// UDP数据方向
/// </summary>
public enum UdpDataDirection
{
    Received,
    Sent
}

/// <summary>
/// UDP连接状态
/// </summary>
public enum UdpConnectionStatus
{
    Stopped,
    Starting,
    Listening,
    Connected,
    Error
}